package com.shuang.backtracking15;

public class SolveSudoku {
    //二维递归 两层for循环
    //第一层for循环遍历行 第二层遍历列
    //递归用来判断空格的地方放1-9是否可行 （本题只要一个结果 递归返回值使用boolean 找到一个树枝下有可行的棋盘填满了直接返回即可 不必搜索整个树）
    public void solveSudoku(char[][] board) {
        //处理
        solveSudokuHelper(board);
    }

    private boolean solveSudokuHelper(char[][] board) {

        //两层for
        //「一个for循环遍历棋盘的行，一个for循环遍历棋盘的列，
        // 一行一列确定下来之后，递归遍历这个位置放9个数字的可能性！」
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                //如果当前位置处是数字直接跳过这个位置 找下个位置
                if (board[i][j] != '.') {
                    continue;
                }

                //当前位置是空白 要填充数字
                for (char k = '1'; k <= '9'; k++) { // (i, j) 这个位置放k是否合适
                    if (isValidSudoku(i, j, k, board)) {
                        //当前位置填1-9 如果合理的话将该位置填上k
                        board[i][j] = k;

                        //递归
                        if (solveSudokuHelper(board)) {
                            // 如果找到合适一组立刻返回
                            return true;
                        }

                        //回溯
                        board[i][j] = '.';
                    }
                }
                // 9个数都试完了，都不行，那么就返回false
                return false;
                // 因为如果一行一列确定下来了，这里尝试了9个数都不行，说明这个棋盘找不到解决数独问题的解！
                // 那么会直接返回， 「这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去！」
            }
        }
        // 遍历完没有返回false，说明棋盘满了并且找到了合适棋盘位置了
        return true;
    }

    /**
     * 判断棋盘是否合法有如下三个维度:
     *     同行是否重复
     *     同列是否重复
     *     9宫格里是否重复
     */
    private boolean isValidSudoku(int row, int col, char val, char[][] board) {
        //检查同行
        for (int i = 0; i < 9; i++) {
            if (board[row][i] == val) {
                return false;
            }
        }

        //检查同列
        for (int j = 0; j < 9; j++) {
            if (board[j][col] == val) {
                return false;
            }
        }

        // 9宫格里是否重复
        int startRow = (row / 3) * 3;
        int startCol = (col / 3) * 3;

        for (int i = startRow; i < startRow + 3; i++) {
            for (int j = startCol; j < startCol + 3; j++) {
                if (board[i][j] == val) {
                    return false;
                }
            }
        }

        //都检查没问题
        return true;

    }
}
